package com.blue.base.web.util;

import com.blue.base.common.enums.ServiceEnum;
import com.blue.base.common.bean.resp.JsonResult;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 图片验证码工具类
 *
 * @author liulei
 * @version 1.0
 */
@Slf4j
public class VerifyCodeUtil {

    private static final String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final Color bgColor = new Color(255, 255, 255);
    private static final int width = 100;
    private static final int height = 40;

    private Random random = new Random();
    private String imageCode;
    private String[] fontNames = {"Arial", "Fira Code", "Consolas ", "微软雅黑"};

    private StringRedisTemplate redisTemplate;

    public VerifyCodeUtil(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 获取随机一个颜色
     */
    private Color randomColor() {
        int red = random.nextInt(150);
        int green = random.nextInt(150);
        int blue = random.nextInt(150);
        return new Color(red, green, blue);
    }

    /**
     * 获取随机一个字体
     */
    private Font randomFont() {
        String name = fontNames[random.nextInt(fontNames.length)];
        int style = random.nextInt(4);
        int size = random.nextInt(5) + 24;
        return new Font(name, style, size);
    }

    /**
     * 获取随机一个字符
     */
    private char randomChar() {
        return codes.charAt(random.nextInt(codes.length()));
    }

    /**
     * 创建一个空白的BufferedImage对象
     */
    private BufferedImage createImage() {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        g2.setColor(bgColor);
        g2.fillRect(0, 0, width, height);
        return image;
    }

    /**
     * 绘制干扰线
     */
    private void drawLine(BufferedImage image) {
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        int num = 5;
        for (int i = 0; i < num; i++) {
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            g2.setColor(randomColor());
            g2.setStroke(new BasicStroke(1.5f));
            g2.drawLine(x1, y1, x2, y2);
        }
    }

    /**
     * 绘制验证码
     */
    private BufferedImage getImage() {
        BufferedImage image = createImage();
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 4; i++) {
            String s = randomChar() + "";
            sb.append(s);
            g2.setColor(randomColor());
            g2.setFont(randomFont());
            float x = i * width * 1.0f / 4;
            g2.drawString(s, x, height - 15);
        }
        this.imageCode = sb.toString();
        drawLine(image);
        return image;
    }

    public String getCode() {
        return imageCode;
    }

    /**
     * 用流传输方法
     */
    private void getCodeImage(BufferedImage image, Map<String, Object> resMap) throws IOException {
        try {
            //返回 base64 数据类型的图片
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ImageIO.write(image, "PNG", bos);
            byte[] bytes = bos.toByteArray();
            Base64.Encoder encoder = Base64.getEncoder();
            String base64Code = encoder.encodeToString(bytes);
            resMap.put("data", "data:image/png;base64," + base64Code);
        } catch (Exception e) {
            log.error("图片验证码生成异常", e);
        }
    }

    /**
     * 生成图片验证码
     */
    public JsonResult generateImageCode() throws IOException {
        Map<String, Object> resMap = Maps.newHashMapWithExpectedSize(2);
        //创建验证码生成器实例取得生成图片和随机字符串
        BufferedImage image = this.getImage();
        String code = this.getCode();
        String codeKey = UUID.randomUUID().toString();
        //图片验证码有效时间【1天】
        redisTemplate.opsForValue().set("redis:code:key:" + codeKey, code, 1, TimeUnit.DAYS);
        //生成图片
        this.getCodeImage(image, resMap);
        resMap.put("code", code);
        resMap.put("codeKey", codeKey);
        return JsonResult.ok(resMap);
    }

    /**
     * 校验图片验证码
     *
     * @param codeKey 验证码key
     * @param code    验证码code
     * @return 校验结果
     */
    public JsonResult verifyImgCode(String codeKey, String code) {
        String cacheCode = redisTemplate.opsForValue().get("redis:code:key:" + codeKey);
        if (null == cacheCode) {
            return JsonResult.failed(ServiceEnum.USER, "校验码已过期，请重新获取");
        }
        if (cacheCode.equalsIgnoreCase(code)) {
            return JsonResult.ok("校验码输入正确");
        }
        return JsonResult.failed(ServiceEnum.USER, "校验码输入错误");
    }
}
